Skip to content

Conversation

@renovate
Copy link
Contributor

@renovate renovate bot commented Oct 10, 2025

This PR contains the following updates:

Package Change Age Confidence
eslint-plugin-react-hooks (source) ^6.0.0 -> ^7.0.0 age confidence

Release Notes

facebook/react (eslint-plugin-react-hooks)

v7.0.1

Compare Source

v7.0.0

Compare Source

This release slims down presets to just 2 configurations (recommended and recommended-latest), and all compiler rules are enabled by default.

  • Breaking: Removed recommended-latest-legacy and flat/recommended configs. The plugin now provides recommended (legacy and flat configs with all recommended rules), and recommended-latest (legacy and flat configs with all recommended rules plus new bleeding edge experimental compiler rules). (@​poteto in #​34757)

Configuration

📅 Schedule: Branch creation - "after 01:00 and before 07:00 every weekday" in timezone Europe/London, Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.


Note

Upgrade eslint-plugin-react-hooks to v7 and refactor components/hooks for rules compliance, improving state init, memoization, and UX around async flows and wizards.

  • Dependencies
    • Bump eslint-plugin-react-hooks from ^6 to ^7 (lockfile updated; add hermes-parser).
  • Frontend/Hook Refactors
    • Replace useEffect-based initializations with lazy useState/useRef (e.g., quit confirmation, toast IDs).
    • Use useWatch from react-hook-form for live form values; add useMemo for derived lists; wrap funcs in useCallback.
    • Improve tooltip trigger ref handling in ui/tooltip.
  • MCP Servers & Chat UI
    • Card: use delay helper for highlight/transition animations; track previous status via ref.
    • Tools modal: init enabled tools without useEffect; track last server.
    • Customize tools table: sync local overrides with props; expand deps for stable callbacks.
    • Error alert: dismiss per-error instance.
  • Forms
    • Local/Remote MCP forms: memoize groups; use useWatch for auth; minor className tweak; loading/submit flows unchanged.
    • Registry remote form: useWatch for auth; keep readiness toasts after close.
  • Logs
    • Make skeleton shimmer deterministic (precomputed counts) to avoid Math.random during render.
  • Experimental/Optimizer
    • Cleanup optimizer: memoize client removal; use memoized groups-with-servers; group selector uses useWatch.
  • Tests
    • Remove explicit timeouts from waitForElementToBeRemoved; assert final toasts and call ordering in registry install wizard.

Written by Cursor Bugbot for commit 09e3d02. This will update automatically on new commits. Configure here.

@renovate renovate bot force-pushed the renovate/major-react-monorepo branch from b9e50fe to 61479b7 Compare October 10, 2025 12:49
@renovate renovate bot force-pushed the renovate/major-react-monorepo branch 2 times, most recently from 7c16b2f to 23a4f0c Compare October 24, 2025 17:49
@renovate renovate bot force-pushed the renovate/major-react-monorepo branch from 23a4f0c to 3fe7969 Compare October 29, 2025 11:10
@renovate renovate bot force-pushed the renovate/major-react-monorepo branch from 3fe7969 to 57e0f7c Compare November 10, 2025 13:58
@renovate renovate bot force-pushed the renovate/major-react-monorepo branch 2 times, most recently from 733962a to 1e10133 Compare November 19, 2025 15:50
@peppescg peppescg force-pushed the renovate/major-react-monorepo branch from 1e10133 to 042d258 Compare November 24, 2025 16:17
@renovate
Copy link
Contributor Author

renovate bot commented Nov 24, 2025

Edited/Blocked Notification

Renovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR.

You can manually request rebase by checking the rebase/retry box above.

⚠️ Warning: custom changes will be lost.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is being reviewed by Cursor Bugbot

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

@peppescg peppescg force-pushed the renovate/major-react-monorepo branch from 1d33724 to 09e3d02 Compare November 26, 2025 12:12
@peppescg
Copy link
Collaborator

@claude review this pr

@claude
Copy link

claude bot commented Nov 26, 2025

Claude finished @peppescg's task —— View job


PR Review: eslint-plugin-react-hooks v7 Upgrade

Overview

This PR upgrades eslint-plugin-react-hooks from v6 to v7 and refactors the codebase to comply with stricter React hooks linting rules. The changes follow React's modern best practices by eliminating unnecessary useEffect calls and replacing them with more direct state management patterns.


Assessment of Bugbot Concerns

1. ❌ Bugbot Issue #1 is INVALID - Timeout cleanup in card-mcp-server/index.tsx:119

Bugbot's claim: Missing timeout cleanup causes memory leak.

Reality: The code DOES properly clean up the async operation:

useEffect(() => {
  if (searchNewServerName === name) {
    let cancelled = false

    const showNewServerAnimation = async () => {
      setIsNewServer(true)
      await delay(2000)
      if (!cancelled) {
        setIsNewServer(false)
      }
    }

    showNewServerAnimation()

    return () => {
      cancelled = true  // ✅ Proper cleanup!
    }
  }
}, [name, searchNewServerName])

The cleanup function sets cancelled = true, which prevents state updates after unmount. This is a correct pattern for cancelling async operations. The delay function is a promise-based utility, not setTimeout, and the cancellation flag prevents the state update from running.

Location: renderer/src/features/mcp-servers/components/card-mcp-server/index.tsx:117-136


2. ✅ Bugbot Issue #2 is VALID but INTENTIONAL - State update during render in use-customize-tools-table.ts:51

Bugbot's claim: Calling state setters during render violates React rules.

Your response (from comments): The new react-hooks linter highlights that useEffect usage was wrong in this case, referencing React docs on "You Might Not Need an Effect".

Analysis: This is a valid React pattern for synchronizing state with props:

// Sync toolsOverride with overrideTools when it changes
if (overrideTools !== lastOverrideTools) {
  setLastOverrideTools(overrideTools)
  if (overrideTools) {
    setToolsOverride(overrideTools)
  }
}

This replaces the old code:

useEffect(() => {
  if (overrideTools) {
    setToolsOverride(overrideTools)
  }
}, [overrideTools])

Why this is correct:

  1. React allows state updates during render for prop synchronization
  2. The pattern uses a "last seen value" tracker to prevent infinite loops
  3. This is explicitly recommended in React's documentation
  4. The v7 linter correctly identified the useEffect as unnecessary

Verdict: This change is intentional and correct per React best practices.

Location: renderer/src/features/mcp-servers/hooks/use-customize-tools-table.ts:45-51


Refactoring Patterns Review

Pattern 1: Replacing useEffect for Initialization

Example in mcp-tools-modal.tsx:

// OLD (removed):
useEffect(() => {
  if (serverTools?.tools) {
    const enabledTools = serverTools.tools
      .filter((tool) => tool.enabled)
      .map((tool) => tool.name)
    setLocalEnabledTools(enabledTools)
  }
}, [serverTools, serverName])

// NEW:
if (serverTools?.tools && lastInitializedServer !== serverName) {
  const enabledTools = serverTools.tools
    .filter((tool) => tool.enabled)
    .map((tool) => tool.name)
  setLocalEnabledTools(enabledTools)
  setLastInitializedServer(serverName)
}

Assessment: ✅ Correct. Uses a "last initialized" tracker to initialize state once per server, avoiding unnecessary effects.

Location: renderer/src/features/chat/components/mcp-tools-modal.tsx:72-79


Pattern 2: Using useRef Instead of useState for Previous Values

Example in card-mcp-server/index.tsx:

// OLD:
const [prevStatus, setPrevStatus] = useState<CoreWorkload['status']>(status)

// NEW:
const prevStatusRef = useRef<CoreWorkload['status']>(status)

Assessment: ✅ Correct. When tracking previous values for comparison (not for rendering), useRef is more appropriate than useState since it doesn't trigger re-renders.

Location: renderer/src/features/mcp-servers/components/card-mcp-server/index.tsx:144


Pattern 3: Async Cleanup Pattern

Example in card-mcp-server/index.tsx:

useEffect(() => {
  if (prevStatusRef.current !== status && ['running'].includes(status ?? '')) {
    let cancelled = false

    const showStatusChangeAnimation = async () => {
      setHadRecentStatusChange(true)
      await delay(2500)
      if (!cancelled) {
        setHadRecentStatusChange(false)
      }
    }

    showStatusChangeAnimation()

    return () => {
      cancelled = true
    }
  }
  prevStatusRef.current = status
}, [status])

Assessment: ✅ Correct. Properly handles cleanup for async animations by checking a cancellation flag before updating state.

Location: renderer/src/features/mcp-servers/components/card-mcp-server/index.tsx:146-169


Pattern 4: Deterministic Rendering

Example in logs-page/index.tsx:

// Generate skeleton counts once to avoid calling Math.random during render
const SKELETON_COUNTS = Array.from(
  { length: 20 },
  () => Math.floor(Math.random() * 6) + 1
)

Assessment: ✅ Excellent. Pre-computes random values outside the component to ensure deterministic rendering. This prevents hydration mismatches and unnecessary re-renders.

Location: renderer/src/features/mcp-servers/sub-pages/logs-page/index.tsx:18-22


Pattern 5: Stable Ref Callback with useCallback

Example in tooltip.tsx:

const setRefs = React.useCallback(
  (node: HTMLButtonElement | null) => {
    if (ctx) {
      const triggerRef = ctx.triggerRef  // Extract ref first
      triggerRef.current = node
    }
    // forward to consumer
    if (typeof forwardedRef === 'function') {
      forwardedRef(node)
    } else if (forwardedRef) {
      ;(forwardedRef as React.MutableRefObject<HTMLButtonElement | null>).current = node
    }
  },
  [ctx, forwardedRef]
)

Assessment: ✅ Good. Properly includes dependencies in useCallback to satisfy the linter while maintaining stable ref handling.

Location: renderer/src/common/components/ui/tooltip.tsx:71-88


Additional Issues Found

⚠️ Minor: Dependency Array in multi-server-install-wizard.tsx

The useEffect at line 49-75 includes createGroupMutation in its dependency array, which is a mutation object from @tanstack/react-query. This object reference changes on every render, potentially causing the effect to run more frequently than intended.

Recommendation: Consider extracting just createGroupMutation.isPending and createGroupMutation.mutateAsync if possible, or ensure the mutation is memoized with stable identity.

Location: renderer/src/features/registry-servers/components/multi-server-install-wizard.tsx:69-75


Summary

Strengths:

  1. Correctly implements React's modern patterns for state synchronization
  2. Eliminates unnecessary effects per React documentation
  3. Proper cleanup for async operations
  4. Good use of useRef for non-rendering state
  5. Deterministic rendering improvements
  6. Follows the principle "You Might Not Need an Effect"

⚠️ Concerns:

  1. One Bugbot warning is invalid (timeout cleanup is correct)
  2. One Bugbot warning is valid but the change is intentional and correct
  3. Minor dependency array concern in wizard component (not blocking)

🎯 Verdict:

APPROVED - This PR correctly implements React hooks best practices and properly addresses the new v7 linting rules. The refactoring follows official React documentation and improves code quality. The Bugbot warnings are either incorrect or flagging intentional, correct patterns.

The author's comment correctly identifies that the new linter is pointing out where useEffect was being misused, and the refactoring addresses this appropriately.


@peppescg peppescg merged commit 76b64ec into main Nov 26, 2025
17 of 19 checks passed
@peppescg peppescg deleted the renovate/major-react-monorepo branch November 26, 2025 13:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants